package v2.parser.load


import util.parsing.combinator.JavaTokenParsers
import v2.parser._

/**
 * @author bweber
 * @version $Rev$, $Date$
 */


  /*
values := value*
value := single | bunch | type-ref | type-def | literal  | link

bunch := type-ref & slots

single := type-ref & slots

slots := slot*
slot := attr-ref & value

type-ref := name
attr-ref := name

link := name+

type-def := name & attr-defs
literal :=  name & type-ref
attr-defs := attr-def*
attr-def := name & type-ref
   */
class Unpickler extends JavaTokenParsers {
  
  def values   : Parser[List[Value]] = "{"~>"\"items\""~>":"~>"["~>repsep(value,",")<~"]"<~"}"
  def value    : Parser[Value]       = bunch | single | typedef | typeref | literal | link
  def link     : Parser[Link]        = "{"~>"\"link\""~>":"~>"["~>repsep(name,",")<~"]"<~"}" ^^ { strs => Link(strs) }
  def bunch    : Parser[Bunch]       = "{"~>"\"bunch\""~>":"~>"{"~>"\"type\""~>":"~>typeref ~ opt(slots)<~"}"<~"}" ^^ 
          {case typeref~slots => slots match {
                case Some(x) => Bunch(typeref,x)
                case _ => Bunch(typeref)
              }
          }
  
  def single   : Parser[Single]      = "{"~>"\"single\""~>":"~>"{"~>"\"type\""~>":"~>typeref ~ opt(slots)<~"}"<~"}" ^^ 
          {case typeref~slots => slots match {
                case Some(x) => Single(typeref,x) 
                case _ => Single(typeref)
              }
          }
                                                                                                                   
  def slots    : Parser[List[Slot]]  = ","~>"\"slots\""~>":"~>"{"~>repsep(slot,",")<~"}"
  def slot     : Parser[Slot]        = attrref ~":"~ value ^^ {case attrref~_~value => Slot(attrref,value) }
  def typeref  : Parser[TypeRef]     = name ^^ {(name) => TypeRef(name) }
  def attrref  : Parser[String]      = name
  def typedef  : Parser[Type]        = "{"~>"\"Type\""~>":"~>"{"~>"\"type\""~>":"~>name ~ opt(attrdefs)<~"}"<~"}" ^^ 
          { case name~attrdefs => attrdefs match {
                case Some(x) => Type(name,x)
                case _ => Type(name)
              }
          }
  def literal  : Parser[Literal]     = "{"~>"\"literal\""~>":"~>name<~"}" ^^ { case name => Type("str"); Literal(name,Type.typeRef("str")) }
  def attrdefs : Parser[List[Attr]]  = ","~>"\"attrs\""~>":"~>"{"~>repsep(attrdef,",")<~"}"
  def attrdef  : Parser[Attr]        = name ~ ":" ~ typeref ^^ { case name~_~typeref => Attr(name,typeref) }
  def name     : Parser[String]      = stringLiteral ^^ { str => str.substring(1,str.length-1) } // todo unescape double quotes
  
  def unparse(str:String) = {
    parse(values,str).get
  }
}

object Unpickler {
  val unpickler = new Unpickler
  
  def apply(str:String) = {
    unpickler.unparse(str)
  }
}

object Pickler {
  val pickler = new Pickler
  def apply(values:List[Value]) = {
    pickler.parse(values)
  }
  def apply(values:Value*) = {
    pickler.parse(values.toList)
  }
}

class Pickler private {
  
  def parse(values: List[Value]):String = {
    parse(values,0)
  }
  
  def dispatchparse(value:Value,depth:Int):String = {
    if ( value.isInstanceOf[Single]) {
      parseSingle(value.asInstanceOf[Single],depth)
    }
    else if ( value.isInstanceOf[Bunch]) {
      parseBunch(value.asInstanceOf[Bunch],depth)
    }
    else if ( value.isInstanceOf[Slot]) {
      parseSlot(value.asInstanceOf[Slot],depth)
    }
    else if ( value.isInstanceOf[TypeRef]) {
      parseTypeRef(value.asInstanceOf[TypeRef])
    }
    else if ( value.isInstanceOf[Type]) {
      parseType(value.asInstanceOf[Type],depth)
    }
    else if ( value.isInstanceOf[Attr]) {
      parseAttr(value.asInstanceOf[Attr],depth)
    }
    else if ( value.isInstanceOf[Literal]) {
      parseLiteral(value.asInstanceOf[Literal],depth)
    }
    else if ( value.isInstanceOf[Link]) {
      parseLink(value.asInstanceOf[Link],depth)
    }
    else {
      throw new RuntimeException("unknown value type: " + value)
    }
  }
  
  def indent(depth:Int):String = {
    var str = ""
    for ( i <- ( 0 until depth ) ) {
      str += " "
    }
    str
  }
  
  def parseBunch(bunch:Bunch,depth:Int):String = {
      val strSoFar = new StringBuilder()
      strSoFar append indent(depth) append "{ \"bunch\" : \n" 
          strSoFar append indent(depth+4) append "{ \"type\" : " append parseTypeRef(bunch.typeref)
              if ( !bunch.slots.isEmpty ) {
                strSoFar append ", \n"
                strSoFar append indent(depth+8) append "\"slots\" : \n" 
                    strSoFar append parseSlots(bunch.slots,depth+8)
              }
              else {
                strSoFar append "\n"
              }
          strSoFar append indent(depth+4) append "}\n" 
      strSoFar append indent(depth) append "}\n"
      strSoFar.toString
  }
  
  def parseSingle(single:Single,depth:Int):String = {
      val strSoFar = new StringBuilder
      strSoFar append indent(depth) append "{ \"single\" : \n" 
          strSoFar append indent(depth+4) append "{ \"type\" : " append parseTypeRef(single.typeref)
              if ( !single.slots.isEmpty ) 
              {
                strSoFar append ", \n"
                strSoFar append indent(depth+8) append "\"slots\" : \n" 
                  strSoFar append parseSlots(single.slots,depth+12)
              }
              else {
                strSoFar append "\n"
              }
          strSoFar append indent(depth+4) append "}\n" 
      strSoFar append indent(depth) append "}\n"
      strSoFar.toString
  }
  
  def parseLink(link:Link,depth:Int):String = {
      var quotedStrings = List[String]()
      for ( str <- link.pointsAt ) {
        quotedStrings =  "\"" + str + "\"" :: quotedStrings
      }
      indent(depth) + "{ \"link\" : [ " + quotedStrings.reverse.mkString(", ") +  " ] }\n"
  }
  
  def parseSlot(slot:Slot,depth:Int):String = {
      indent(depth) + parseString(slot.attrref) + " : \n" + 
          dispatchparse(slot.value,depth+4)
  }
  
  def parseTypeRef(typeref:TypeRef):String = {
      parseString(typeref.name)
  }
  
  def parseLiteral(literal:Literal,depth:Int):String = {
    indent(depth) + "{ \"literal\" : " + parseString(literal.value) + " }\n"
  }
  
  def parseType(_type:Type,depth:Int):String = {
     val strSoFar = new StringBuilder()
     strSoFar append indent(depth) append "{ \"Type\" : \n" 
         strSoFar append indent(depth+4) append "{ \"type\" : " append parseString(_type.name)
             if ( !_type.attrdefs.isEmpty ) {
               strSoFar append ", \n" append parseAttrs(_type.attrdefs,depth+8)
             }
             else {
               strSoFar append "\n"
             }
         strSoFar append indent(depth+4) append "}\n"
     strSoFar append indent(depth) append "}\n"
     strSoFar.toString
  }
  
  def parseSlots(slots:List[Slot],depth:Int):String = {
    val strSoFar = new StringBuilder()
    var removeLastComma = false
    strSoFar append indent(depth) append "{\n"
    slots.foreach { slot => removeLastComma = true; strSoFar append parseSlot(slot,depth+4) + indent(depth+4) + ",\n" }
    if ( removeLastComma ) {
      strSoFar.deleteCharAt(strSoFar.length-1)
      strSoFar.deleteCharAt(strSoFar.length-1)
      for ( i <- 0 until depth+4 ) {
        strSoFar.deleteCharAt(strSoFar.length-1)
      }
    }
    strSoFar append indent(depth) append "}\n"
    strSoFar.toString
  }
  
  def parse(values:List[Value],depth:Int):String = {
    val strSoFar = new StringBuilder()
    var removeLastComma = false
    strSoFar append indent(depth) append "{ \"items\" : \n" append indent(depth+4) append "[\n"
    values.foreach { value => removeLastComma = true; strSoFar append dispatchparse(value,depth+8) + indent(depth+8) + ",\n" }
    if ( removeLastComma ) {
      strSoFar.deleteCharAt(strSoFar.length-1)
      strSoFar.deleteCharAt(strSoFar.length-1)
      for ( i <- 0 until depth+8 ) {
        strSoFar.deleteCharAt(strSoFar.length-1)
      }
    }
    strSoFar append indent(depth+4) append "]\n" append indent(depth) append "}\n"
    strSoFar.toString
  }
  
  def parseAttrs(attrs:List[Attr],depth:Int):String = {
    val strSoFar = new StringBuilder()
    var removeLastComma = false
    strSoFar append indent(depth) append "\"attrs\" : { \n"
    attrs.foreach { attr => removeLastComma = true; strSoFar append parseAttr(attr,depth+4) append ",\n" }
    if ( removeLastComma ) {
      strSoFar.deleteCharAt(strSoFar.length-2)
      strSoFar.deleteCharAt(strSoFar.length-1)
      strSoFar append "\n"
    }
    strSoFar append indent(depth) append "}\n"
    strSoFar.toString
  }
  
  def parseAttr(attr:Attr,depth:Int):String = {
    indent(depth) + parseString(attr.name) + " : " + parseString(attr.typeref.name)
  }
  
  // todo escape any double quotes!
  def parseString(str:String):String = {
     "\"" + str + "\""
  }
}